/////////////////////////////////////////////////////////////////////////
// $Id: cpu.cc 11202 2012-06-03 18:46:20Z sshwarts $
/////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2001-2012  The Bochs Project
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA B 02110-1301 USA
/////////////////////////////////////////////////////////////////////////

#define NEED_CPU_REG_SHORTCUTS 1
#include "bochs.h"
#include "cpu.h"
#include "stats.h"
#include "compiler.h"
#define LOG_THIS BX_CPU_THIS_PTR

#define InstrumentICACHE 0

#if InstrumentICACHE
static unsigned iCacheLookups=0;
static unsigned iCacheMisses=0;

#define InstrICache_StatsMask 0xffffff

#define InstrICache_Stats() {\
  if ((iCacheLookups & InstrICache_StatsMask) == 0) { \
    BX_INFO(("ICACHE lookups: %u, misses: %u, hit rate = %6.2f%% ", \
          iCacheLookups, \
          iCacheMisses,  \
          (iCacheLookups-iCacheMisses) * 100.0 / iCacheLookups)); \
    iCacheLookups = iCacheMisses = 0; \
  } \
}
#define InstrICache_Increment(v) (v)++
#else
#define InstrICache_Stats()
#define InstrICache_Increment(v)
#endif

/// Used for JIT callout.
extern "C" void BochsCall(BX_CPU_C* processor, bxInstruction_c *instr)
{
  processor->execute(instr);
}
extern "C" void NakedBochsCall(BX_CPU_C* processor, bxInstruction_c *instr, Bit64u opaque)
{
  processor->execute_naked(instr, opaque);
}
extern "C" void GetResolveModRM(BX_CPU_C* processor, bxInstruction_c *instr, Bit64u opaque)
{
  processor->resolve_modrm(instr, opaque);
}
extern "C" void GetModRMXlation(BX_CPU_C* processor, bxInstruction_c *instr, Bit64u opaque)
{
  processor->modrm_xlation(instr, opaque);
}

#if BX_SUPPORT_SMP

void BX_CPU_C::cpu_handle_abnormal_return(void)
{
  if (BX_CPU_THIS_PTR what_am_i_doing & BX_BUILD_ICACHE_ENTRY)
  {
     // If this processor is building icache entry. it must 
     // have reached here due to the fault in boundaryFetch.
     // release the shared icache write lock.
     BX_CPU_THIS_PTR current_entry->pAddr = BX_ICACHE_INVALID_PHY_ADDRESS;
     BX_CPU_THIS_PTR current_entry->release_entry_rwlock();
     BX_CPU_THIS_PTR siCache->release_icache_rwlock();
     BX_CPU_THIS_PTR what_am_i_doing &= ~BX_BUILD_ICACHE_ENTRY;
     BX_CPU_THIS_PTR current_entry = 0;
  }
  
  if (BX_CPU_THIS_PTR addr_locked.paddress)
  {
    // The processor faults while locking pages. unlock the pages.
    bx_phy_address pAddr = BX_CPU_THIS_PTR addr_locked.paddress;
    BX_CPU_THIS_PTR bx_tlb_release_shared_access(pAddr, 1);
    BX_CPU_THIS_PTR bx_tlb_reset_address_locked();
  }

  if (BX_CPU_THIS_PTR current_entry)
  {
     // The processor is currently executing a trace.
     // release the read lock on it.
     BX_CPU_THIS_PTR current_entry->release_entry_rwlock();
     BX_CPU_THIS_PTR current_entry = 0;
  }

  BX_CPU_THIS_PTR what_am_i_doing &= ~BX_IN_JIT; 
}

void BX_CPU_C::handleAssistAsyncEvent(void)
{
  // Pop all events off the queue and handle them one by one.
  bx_CPU_event *event = 0;
  bxICacheEntry_c *entry = 0;
  while(event = BX_CPU_THIS_PTR assist_events.pop_event())
  {
    unsigned type = event->event_type;
    void *payload = event->event_payload;
    Bit32u opaque = event->event_opaque;
    switch(type)
    {
    /// SMC event.
    case BX_ASYNC_EVENT_SMC_STOP:
      entry = (bxICacheEntry_c*)payload;
      entry->annul();
      BX_CPU_THIS_PTR stats->smc_events ++;
      break;
    /// TLB flush event.
    case BX_ASYNC_EVENT_TLB_FLUSH:
      // flush the entire TLB.
      if (opaque == BX_INVALID_PHY_ADDRESS) BX_CPU_THIS_PTR TLB_flush(); 
      // flush TLB to this address.
      else BX_CPU_THIS_PTR TLB_invlpg(opaque);
      BX_CPU_THIS_PTR stats->tlb_events ++;
      break;
    /// CPU Reset Event.
    case BX_ASYNC_EVENT_CPU_RESET:
      BX_CPU_THIS_PTR reset(opaque);
      BX_CPU_THIS_PTR stats->reset_events ++;
      break;
    /// ICache Flush Event.
    case BX_ASYNC_EVENT_ICACHE_FLUSH:
      BX_INFO(("Flush Interpreter Code Cache"));
      BX_CPU_THIS_PTR siCache->flushICacheEntries();
      BX_CPU_THIS_PTR siCache->pageWriteStampTable->resetWriteStamps();
      BX_CPU_THIS_PTR stats->icache_flush_events ++;
      break;
    /// Unknown event.
    default:
      BX_ASSERT(0);
      break;
    }
    // lastly. free the event structure.
    free_cpu_event(event);
    BX_CPU_THIS_PTR stats->assist_events ++;
  }
}

void BX_CPU_C::handleDebugEvent()
{
  bx_CPU_event *event = 0;
  bxCPUControl_c *debug = (bxCPUControl_c*) bx_pc_system.debug;

  pthread_mutex_lock(&bx_pc_system.debug_lock);

  // Flush the code cache.
  if (debug->flushcc)
  {
    debug->flushcc = 0;
    event=BX_CPU_THIS_PTR create_cpu_event(BX_ASYNC_EVENT_ICACHE_FLUSH, 
                                           0,
                                           BX_ASSIST_EVENT_NONE);
    BX_CPU_THIS_PTR assist_events.push_event(event);

    // also set the async_event. async_event is checked by the JITed code.
    BX_CPU_THIS_PTR async_event |= BX_ASYNC_EVENT_STOP_TRACE;
  }

  // No interpreter entry cache.
  if (debug->nocache)
  {
    debug->nocache = 0;
    bx_pc_system.nocache = 1;
  }

  pthread_mutex_unlock(&bx_pc_system.debug_lock);
}

void BX_CPU_C::execute(bxInstruction_c *i)
{
  // want to allow changing of the instruction inside instrumentation callback
  BX_INSTR_BEFORE_EXECUTION(BX_CPU_ID, i);

  // execute the instruction.
  RIP += i->ilen();
  BX_CPU_CALL_METHOD(i->execute, (i)); // might iterate repeat instruction
  BX_CPU_THIS_PTR prev_rip = RIP; // commit new RIP

  BX_INSTR_AFTER_EXECUTION(BX_CPU_ID, i);
  BX_CPU_THIS_PTR icount++;
}

void BX_CPU_C::execute_naked(bxInstruction_c *i, Bit64u opaque)
{
  // execute the instruction.
  BX_CPU_CALL_METHOD(i->execute, (i)); // might iterate repeat instruction
  // bx_address eaddr = BX_CPU_CALL_METHODR(i->ResolveModrm, (i));
}

void BX_CPU_C::resolve_modrm(bxInstruction_c *i, Bit64u opaque)
{
  bx_address eaddr = BX_CPU_CALL_METHODR(i->ResolveModrm, (i));
  bx_address laddr = get_laddr(i->seg(), eaddr);  
  // store it to the address to translate.
  BX_CPU_THIS_PTR address_to_xlate = laddr;
}

void BX_CPU_C::modrm_xlation(bxInstruction_c *i, Bit64u opaque)
{
  BX_CPU_THIS_PTR address_xlated = 0;
  bx_address laddr = BX_CPU_THIS_PTR address_to_xlate;
  unsigned tlbIndex = BX_TLB_INDEX_OF(laddr, 1);
  bx_TLB_entry *tlbEntry = &BX_CPU_THIS_PTR TLB.entry[tlbIndex];
  if (tlbEntry->lpf == LPFOf(laddr)) 
  {
    // See if TLB privilege level allows us read access from this CPL.
    if (!(tlbEntry->accessBits & USER_PL)) 
    { 
      BX_CPU_THIS_PTR address_xlated = tlbEntry->hostPageAddr | PAGE_OFFSET(laddr);
    }
  }
}

void BX_CPU_C::compile_submit(bxICacheEntry_c *entry)
{
  // this entry is hot enough to be compiled
  if (!bx_pc_system.nojit && !entry->submitted && 
     entry->icount > bx_pc_system.icount && entry->pAddr > bx_pc_system.jit_min && 
     entry->pAddr < bx_pc_system.jit_max && 
     !entry->cross_page_boundary() && 
     !entry->repeat_instruction())
  {
    entry->submitted = 1;
    BX_COMP(0)->compiler_submit(entry);
  }
}

void BX_CPU_C::cpu_run_compiled_trace(bxICacheEntry_c *entry)
{
  BX_CPU_THIS_PTR what_am_i_doing |=  BX_IN_JIT; 
  BX_CODE_CACHE_EXEC(this, entry->raddress);
  BX_CPU_THIS_PTR what_am_i_doing &= ~BX_IN_JIT; 
}

void BX_CPU_C::cpu_run_interpret_trace(bxICacheEntry_c *entry)
{
  bxInstruction_c *i = &entry->i[0];
  bxInstruction_c *last = i + (entry->tlen);

  BX_CPU_THIS_PTR current_entry->icount++;

  for(;;) 
  {
    BX_CPU_THIS_PTR current_instr = i;
    // want to allow changing of the instruction inside instrumentation callback
    BX_INSTR_BEFORE_EXECUTION(BX_CPU_ID, i);

    RIP += i->ilen();
    BX_CPU_CALL_METHOD(i->execute, (i)); // might iterate repeat instruction
    BX_CPU_THIS_PTR prev_rip = RIP; // commit new RIP

    BX_INSTR_AFTER_EXECUTION(BX_CPU_ID, i);
    BX_CPU_THIS_PTR icount++;
    BX_CPU_THIS_PTR current_instr = 0;

    if (BX_CPU_THIS_PTR async_event) 
    {
      // clear stop trace magic indication.
      BX_CPU_THIS_PTR async_event &= ~BX_ASYNC_EVENT_STOP_TRACE;
      break;
    }

    if (BX_CPU_THIS_PTR assist_events.has_event())
    {
       // Events will be processed the next time 
       // the processor tries to execute.
       break;
    }

    if (++i == last) break;
  }

  // Should this entry be compiled ?
  BX_CPU_THIS_PTR compile_submit(current_entry);
}

void BX_CPU_C::cpu_run_trace(void)
{
  if (setjmp(BX_CPU_THIS_PTR jmp_buf_env)) 
  {
    // can get here only from exception function or VMEXIT
    BX_CPU_THIS_PTR cpu_handle_abnormal_return();
    // Lastly, executed one more instruction.
    BX_CPU_THIS_PTR icount++;
    return;
  }

  // Events that are interesting to the emulator.
  if (BX_CPU_THIS_PTR assist_events.has_event()) 
  {
    BX_CPU_THIS_PTR stats->interp_aexit ++;
    handleAssistAsyncEvent();
  }

  // check on events which occurred for previous instructions (traps)
  // and ones which are asynchronous to the CPU (hardware interrupts)
  if (BX_CPU_THIS_PTR async_event) 
  {
    if (handleAsyncEvent()) 
    {
      // If request to return to caller ASAP.
      return;
    }
  }

  bxICacheEntry_c *entry = getICacheEntry();

  BX_CPU_THIS_PTR current_entry = entry;

  if (entry->raddress)
  {
    // there is a compiled code for this entry.
    BX_CPU_THIS_PTR cpu_run_compiled_trace(entry);
    BX_CPU_THIS_PTR stats->comp_icount += entry->tlen;
    BX_CPU_THIS_PTR icount += entry->tlen;
  }
  else 
  {
    // there is no compiled code for this entry.
    // execute using interpretation.
    BX_CPU_THIS_PTR cpu_run_interpret_trace(entry);
    BX_CPU_THIS_PTR stats->interp_icount += entry->tlen;
  }

  if (BX_CPU_THIS_PTR current_entry)
  {
    // The processor is currently executing a trace.
    // release the read lock on it as it is no
    // longer needed.
    BX_CPU_THIS_PTR current_entry->release_entry_rwlock();
    BX_CPU_THIS_PTR current_entry = 0;
  }

  // At last, is there any pending debug event ?
  BX_CPU_THIS_PTR handleDebugEvent();
}

#endif

bxICacheEntry_c* BX_CPU_C::getICacheEntry(void)
{
  bx_address eipBiased = RIP + BX_CPU_THIS_PTR eipPageBias;

  if (eipBiased >= BX_CPU_THIS_PTR eipPageWindowSize) {
    prefetch();
    eipBiased = RIP + BX_CPU_THIS_PTR eipPageBias;
  }

  bx_phy_address pAddr = BX_CPU_THIS_PTR pAddrFetchPage + eipBiased;

  // 1. Find the entry in the private cache and hold read lock on it.
  if (bx_pc_system.privcache)
  { 
    BX_CPU_THIS_PTR current_entry = BX_CPU_THIS_PTR piCache->get_entry_and_use(pAddr, BX_CPU_THIS_PTR fetchModeMask);
    if (BX_CPU_THIS_PTR current_entry->pAddr == pAddr)
    {
      // This is the entry to use.
      BX_CPU_THIS_PTR stats->priv_entry ++;
      return BX_CPU_THIS_PTR current_entry;
    }
    else 
    {
      // Release the read lock on this entry.
      BX_CPU_THIS_PTR current_entry->release_entry_rwlock();
    }
  }

reprobe:
  // 2. Find an entry in the shared cache and hold read lock on it.
  BX_CPU_THIS_PTR current_entry = BX_CPU_THIS_PTR siCache->get_entry_and_use(pAddr, BX_CPU_THIS_PTR fetchModeMask);

  InstrICache_Increment(iCacheLookups);
  InstrICache_Stats();

  if (BX_CPU_THIS_PTR current_entry->pAddr != pAddr)
  {
    // iCache miss. No validated instruction with matching fetch parameters
    // is in the iCache.
    InstrICache_Increment(iCacheMisses);

    // Not the entry wanted. free its read lock.
    BX_CPU_THIS_PTR current_entry->release_entry_rwlock();
    BX_CPU_THIS_PTR current_entry = 0;
  }
  else 
  {
    if (bx_pc_system.privcache) 
    {
       // Found the entry in the shared cache. 
       BX_CPU_THIS_PTR current_entry->hotness[bx_cpuid] ++;
       if (BX_CPU_THIS_PTR current_entry->hotness[bx_cpuid] >  bx_pc_system.privcount)
       {
          BX_CPU_THIS_PTR current_entry->private_promote(bx_cpuid);
          BX_CPU_THIS_PTR current_entry->hotness[bx_cpuid] = 0;
       }
    }
    // This is the entry to use.
    BX_CPU_THIS_PTR stats->shared_entry ++;
    return BX_CPU_THIS_PTR current_entry;
  }

  // Build a new entry.
  BX_CPU_THIS_PTR stats->icache_miss ++;
  serveICacheMiss((Bit32u) eipBiased, pAddr);

  goto reprobe;

  return 0; // Makes the compiler happy.
}

#define BX_REPEAT_TIME_UPDATE_INTERVAL (BX_MAX_TRACE_LENGTH-1)

void BX_CPP_AttrRegparmN(2) BX_CPU_C::repeat(bxInstruction_c *i, BxRepIterationPtr_tR execute)
{
  // non repeated instruction
  if (! i->repUsedL()) {
    BX_CPU_CALL_REP_ITERATION(execute, (i));
    return;
  }

#if BX_X86_DEBUGGER
  BX_CPU_THIS_PTR in_repeat = 0;
#endif

#if BX_SUPPORT_X86_64
  if (i->as64L()) {
    while(1) {
      if (RCX != 0) {
        BX_CPU_CALL_REP_ITERATION(execute, (i));
        BX_INSTR_REPEAT_ITERATION(BX_CPU_ID, i);
        RCX --;
      }
      if (RCX == 0) return;

#if BX_DEBUGGER == 0
      if (BX_CPU_THIS_PTR async_event)
#endif
        break; // exit always if debugger enabled

      BX_CPU_THIS_PTR icount++;

      BX_SYNC_TIME_IF_SINGLE_PROCESSOR(BX_REPEAT_TIME_UPDATE_INTERVAL);
    }
  }
  else
#endif
  if (i->as32L()) {
    while(1) {
      if (ECX != 0) {
        BX_CPU_CALL_REP_ITERATION(execute, (i));
        BX_INSTR_REPEAT_ITERATION(BX_CPU_ID, i);
        RCX = ECX - 1;
      }
      if (ECX == 0) return;

#if BX_DEBUGGER == 0
      if (BX_CPU_THIS_PTR async_event)
#endif
        break; // exit always if debugger enabled

      BX_CPU_THIS_PTR icount++;

      BX_SYNC_TIME_IF_SINGLE_PROCESSOR(BX_REPEAT_TIME_UPDATE_INTERVAL);
    }
  }
  else  // 16bit addrsize
  {
    while(1) {
      if (CX != 0) {
        BX_CPU_CALL_REP_ITERATION(execute, (i));
        BX_INSTR_REPEAT_ITERATION(BX_CPU_ID, i);
        CX --;
      }
      if (CX == 0) return;

#if BX_DEBUGGER == 0
      if (BX_CPU_THIS_PTR async_event)
#endif
        break; // exit always if debugger enabled

      BX_CPU_THIS_PTR icount++;

      BX_SYNC_TIME_IF_SINGLE_PROCESSOR(BX_REPEAT_TIME_UPDATE_INTERVAL);
    }
  }

#if BX_X86_DEBUGGER
  BX_CPU_THIS_PTR in_repeat = 1;
#endif

  RIP = BX_CPU_THIS_PTR prev_rip; // repeat loop not done, restore RIP

  // assert magic async_event to stop trace execution
  BX_CPU_THIS_PTR async_event |= BX_ASYNC_EVENT_STOP_TRACE;
}

void BX_CPP_AttrRegparmN(2) BX_CPU_C::repeat_ZF(bxInstruction_c *i, BxRepIterationPtr_tR execute)
{
  unsigned rep = i->repUsedValue();

  // non repeated instruction
  if (! rep) {
    BX_CPU_CALL_REP_ITERATION(execute, (i));
    return;
  }

#if BX_X86_DEBUGGER
  BX_CPU_THIS_PTR in_repeat = 0;
#endif

  if (rep == 3) { /* repeat prefix 0xF3 */
#if BX_SUPPORT_X86_64
    if (i->as64L()) {
      while(1) {
        if (RCX != 0) {
          BX_CPU_CALL_REP_ITERATION(execute, (i));
          BX_INSTR_REPEAT_ITERATION(BX_CPU_ID, i);
          RCX --;
        }
        if (! get_ZF() || RCX == 0) return;

#if BX_DEBUGGER == 0
        if (BX_CPU_THIS_PTR async_event)
#endif
          break; // exit always if debugger enabled

        BX_CPU_THIS_PTR icount++;

        BX_SYNC_TIME_IF_SINGLE_PROCESSOR(BX_REPEAT_TIME_UPDATE_INTERVAL);
      }
    }
    else
#endif
    if (i->as32L()) {
      while(1) {
        if (ECX != 0) {
          BX_CPU_CALL_REP_ITERATION(execute, (i));
          BX_INSTR_REPEAT_ITERATION(BX_CPU_ID, i);
          RCX = ECX - 1;
        }
        if (! get_ZF() || ECX == 0) return;

#if BX_DEBUGGER == 0
        if (BX_CPU_THIS_PTR async_event)
#endif
          break; // exit always if debugger enabled

        BX_CPU_THIS_PTR icount++;

        BX_SYNC_TIME_IF_SINGLE_PROCESSOR(BX_REPEAT_TIME_UPDATE_INTERVAL);
      }
    }
    else  // 16bit addrsize
    {
      while(1) {
        if (CX != 0) {
          BX_CPU_CALL_REP_ITERATION(execute, (i));
          BX_INSTR_REPEAT_ITERATION(BX_CPU_ID, i);
          CX --;
        }
        if (! get_ZF() || CX == 0) return;

#if BX_DEBUGGER == 0
        if (BX_CPU_THIS_PTR async_event)
#endif
          break; // exit always if debugger enabled

        BX_CPU_THIS_PTR icount++;

        BX_SYNC_TIME_IF_SINGLE_PROCESSOR(BX_REPEAT_TIME_UPDATE_INTERVAL);
      }
    }
  }
  else {          /* repeat prefix 0xF2 */
#if BX_SUPPORT_X86_64
    if (i->as64L()) {
      while(1) {
        if (RCX != 0) {
          BX_CPU_CALL_REP_ITERATION(execute, (i));
          BX_INSTR_REPEAT_ITERATION(BX_CPU_ID, i);
          RCX --;
        }
        if (get_ZF() || RCX == 0) return;

#if BX_DEBUGGER == 0
        if (BX_CPU_THIS_PTR async_event)
#endif
          break; // exit always if debugger enabled

        BX_CPU_THIS_PTR icount++;

        BX_SYNC_TIME_IF_SINGLE_PROCESSOR(BX_REPEAT_TIME_UPDATE_INTERVAL);
      }
    }
    else
#endif
    if (i->as32L()) {
      while(1) {
        if (ECX != 0) {
          BX_CPU_CALL_REP_ITERATION(execute, (i));
          BX_INSTR_REPEAT_ITERATION(BX_CPU_ID, i);
          RCX = ECX - 1;
        }
        if (get_ZF() || ECX == 0) return;

#if BX_DEBUGGER == 0
        if (BX_CPU_THIS_PTR async_event)
#endif
          break; // exit always if debugger enabled

        BX_CPU_THIS_PTR icount++;

        BX_SYNC_TIME_IF_SINGLE_PROCESSOR(BX_REPEAT_TIME_UPDATE_INTERVAL);
      }
    }
    else  // 16bit addrsize
    {
      while(1) {
        if (CX != 0) {
          BX_CPU_CALL_REP_ITERATION(execute, (i));
          BX_INSTR_REPEAT_ITERATION(BX_CPU_ID, i);
          CX --;
        }
        if (get_ZF() || CX == 0) return;

#if BX_DEBUGGER == 0
        if (BX_CPU_THIS_PTR async_event)
#endif
          break; // exit always if debugger enabled

        BX_CPU_THIS_PTR icount++;

        BX_SYNC_TIME_IF_SINGLE_PROCESSOR(BX_REPEAT_TIME_UPDATE_INTERVAL);
      }
    }
  }

#if BX_X86_DEBUGGER
  BX_CPU_THIS_PTR in_repeat = 1;
#endif

  RIP = BX_CPU_THIS_PTR prev_rip; // repeat loop not done, restore RIP

  // assert magic async_event to stop trace execution
  BX_CPU_THIS_PTR async_event |= BX_ASYNC_EVENT_STOP_TRACE;
}

// boundaries of consideration:
//
//  * physical memory boundary: 1024k (1Megabyte) (increments of...)
//  * A20 boundary:             1024k (1Megabyte)
//  * page boundary:            4k
//  * ROM boundary:             2k (dont care since we are only reading)
//  * segment boundary:         any

void BX_CPU_C::prefetch(void)
{
  bx_address laddr;
  unsigned pageOffset;

#if BX_SUPPORT_X86_64
  if (long64_mode()) {
    if (! IsCanonical(RIP)) {
      BX_ERROR(("prefetch: #GP(0): RIP crossed canonical boundary"));
      exception(BX_GP_EXCEPTION, 0);
    }

    // linear address is equal to RIP in 64-bit long mode
    pageOffset = PAGE_OFFSET(EIP);
    laddr = RIP;

    // Calculate RIP at the beginning of the page.
    BX_CPU_THIS_PTR eipPageBias = pageOffset - RIP;
    BX_CPU_THIS_PTR eipPageWindowSize = 4096;
  }
  else
#endif
  {
    BX_CLEAR_64BIT_HIGH(BX_64BIT_REG_RIP); /* avoid 32-bit EIP wrap */
    laddr = get_laddr32(BX_SEG_REG_CS, EIP);
    pageOffset = PAGE_OFFSET(laddr);

    // Calculate RIP at the beginning of the page.
    BX_CPU_THIS_PTR eipPageBias = (bx_address) pageOffset - EIP;

    Bit32u limit = BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].cache.u.segment.limit_scaled;
    if (EIP > limit) {
      BX_ERROR(("prefetch: EIP [%08x] > CS.limit [%08x]", EIP, limit));
      exception(BX_GP_EXCEPTION, 0);
    }

    BX_CPU_THIS_PTR eipPageWindowSize = 4096;
    if (limit + BX_CPU_THIS_PTR eipPageBias < 4096) {
      BX_CPU_THIS_PTR eipPageWindowSize = (Bit32u)(limit + BX_CPU_THIS_PTR eipPageBias + 1);
    }
  }

#if BX_X86_DEBUGGER
  if (hwbreakpoint_check(laddr, BX_HWDebugInstruction, BX_HWDebugInstruction)) {
    BX_CPU_THIS_PTR async_event = 1;
    BX_CPU_THIS_PTR codebp = 1;
    if (! interrupts_inhibited(BX_INHIBIT_DEBUG)) {
       // The next instruction could already hit a code breakpoint but
       // async_event won't take effect immediatelly.
       // Check if the next executing instruction hits code breakpoint

       // check only if not fetching page cross instruction
       // this check is 32-bit wrap safe as well
       if (EIP == (Bit32u) BX_CPU_THIS_PTR prev_rip) {
         if (code_breakpoint_match(laddr)) exception(BX_DB_EXCEPTION, 0);
       }
    }
  }
  else {
    BX_CPU_THIS_PTR codebp = 0;
  }
#endif

  BX_CPU_THIS_PTR clear_RF();

  bx_address lpf = LPFOf(laddr);
  unsigned TLB_index = BX_TLB_INDEX_OF(lpf, 0);
  bx_TLB_entry *tlbEntry = &BX_CPU_THIS_PTR TLB.entry[TLB_index];
  Bit8u *fetchPtr = 0;

  if ((tlbEntry->lpf == lpf) && !(tlbEntry->accessBits & (0x4 | USER_PL))) {
    BX_CPU_THIS_PTR pAddrFetchPage = tlbEntry->ppf;
    fetchPtr = (Bit8u*) tlbEntry->hostPageAddr;
  }  
  else {
    bx_phy_address pAddr = translate_linear(laddr, USER_PL, BX_EXECUTE);
    BX_CPU_THIS_PTR pAddrFetchPage = PPFOf(pAddr);
  }

  if (fetchPtr) {
    BX_CPU_THIS_PTR eipFetchPtr = fetchPtr;
  }
  else {
    BX_CPU_THIS_PTR eipFetchPtr = (const Bit8u*) getHostMemAddr(BX_CPU_THIS_PTR pAddrFetchPage, BX_EXECUTE);

    // Sanity checks
    if (! BX_CPU_THIS_PTR eipFetchPtr) {
      bx_phy_address pAddr = BX_CPU_THIS_PTR pAddrFetchPage + pageOffset;
      if (pAddr >= BX_MEM(0)->get_memory_len()) {
        BX_PANIC(("prefetch: running in bogus memory, pAddr=0x" FMT_PHY_ADDRX, pAddr));
      }
      else {
        BX_PANIC(("prefetch: getHostMemAddr vetoed direct read, pAddr=0x" FMT_PHY_ADDRX, pAddr));
      }
    }
  }
}

bx_CPU_event* BX_CPU_C::create_cpu_event(Bit32u type, void* payload, Bit64u opaque)
{
  bx_CPU_event* event = new bx_CPU_event;
  event->event_type = type;
  event->event_payload = payload;
  event->event_opaque = opaque;
  return event;
}

void BX_CPU_C::free_cpu_event(bx_CPU_event* event) 
{ 
  delete event;
}

#if BX_DEBUGGER || BX_GDBSTUB
bx_bool BX_CPU_C::dbg_instruction_epilog(void)
{
#if BX_DEBUGGER
  bx_address debug_eip = RIP;
  Bit16u cs = BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value;

  BX_CPU_THIS_PTR guard_found.cs  = cs;
  BX_CPU_THIS_PTR guard_found.eip = debug_eip;
  BX_CPU_THIS_PTR guard_found.laddr = get_laddr(BX_SEG_REG_CS, debug_eip);
  BX_CPU_THIS_PTR guard_found.code_32_64 = BX_CPU_THIS_PTR fetchModeMask;

  //
  // Take care of break point conditions generated during instruction execution
  //

  // Check if we hit read/write or time breakpoint
  if (BX_CPU_THIS_PTR break_point) {
    Bit64u tt = bx_pc_system.time_ticks();
    switch (BX_CPU_THIS_PTR break_point) {
    case BREAK_POINT_TIME:
      BX_INFO(("[" FMT_LL "d] Caught time breakpoint", tt));
      BX_CPU_THIS_PTR stop_reason = STOP_TIME_BREAK_POINT;
      return(1); // on a breakpoint
    case BREAK_POINT_READ:
      BX_INFO(("[" FMT_LL "d] Caught read watch point", tt));
      BX_CPU_THIS_PTR stop_reason = STOP_READ_WATCH_POINT;
      return(1); // on a breakpoint
    case BREAK_POINT_WRITE:
      BX_INFO(("[" FMT_LL "d] Caught write watch point", tt));
      BX_CPU_THIS_PTR stop_reason = STOP_WRITE_WATCH_POINT;
      return(1); // on a breakpoint
    default:
      BX_PANIC(("Weird break point condition"));
    }
  }

  if (BX_CPU_THIS_PTR magic_break) {
    BX_INFO(("[" FMT_LL "d] Stopped on MAGIC BREAKPOINT", bx_pc_system.time_ticks()));
    BX_CPU_THIS_PTR stop_reason = STOP_MAGIC_BREAK_POINT;
    return(1); // on a breakpoint
  }

  // see if debugger requesting icount guard 
  if (bx_guard.guard_for & BX_DBG_GUARD_ICOUNT) {
    if (get_icount() >= BX_CPU_THIS_PTR guard_found.icount_max) {
      return(1);
    }
  }

  // convenient point to see if user requested debug break or typed Ctrl-C
  if (bx_guard.interrupt_requested) {
    return(1);
  }

  // support for 'show' command in debugger
  extern unsigned dbg_show_mask;
  if(dbg_show_mask) {
    int rv = bx_dbg_show_symbolic();
    if (rv) return(rv);
  }

  // Just committed an instruction, before fetching a new one
  // see if debugger is looking for iaddr breakpoint of any type
  if (bx_guard.guard_for & BX_DBG_GUARD_IADDR_ALL) {
#if (BX_DBG_MAX_VIR_BPOINTS > 0)
    if (bx_guard.guard_for & BX_DBG_GUARD_IADDR_VIR) {
      for (unsigned n=0; n<bx_guard.iaddr.num_virtual; n++) {
        if (bx_guard.iaddr.vir[n].enabled &&
           (bx_guard.iaddr.vir[n].cs  == cs) &&
           (bx_guard.iaddr.vir[n].eip == debug_eip))
        {
          BX_CPU_THIS_PTR guard_found.guard_found = BX_DBG_GUARD_IADDR_VIR;
          BX_CPU_THIS_PTR guard_found.iaddr_index = n;
          return(1); // on a breakpoint
        }
      }
    }
#endif
#if (BX_DBG_MAX_LIN_BPOINTS > 0)
    if (bx_guard.guard_for & BX_DBG_GUARD_IADDR_LIN) {
      for (unsigned n=0; n<bx_guard.iaddr.num_linear; n++) {
        if (bx_guard.iaddr.lin[n].enabled &&
           (bx_guard.iaddr.lin[n].addr == BX_CPU_THIS_PTR guard_found.laddr))
        {
          BX_CPU_THIS_PTR guard_found.guard_found = BX_DBG_GUARD_IADDR_LIN;
          BX_CPU_THIS_PTR guard_found.iaddr_index = n;
          return(1); // on a breakpoint
        }
      }
    }
#endif
#if (BX_DBG_MAX_PHY_BPOINTS > 0)
    if (bx_guard.guard_for & BX_DBG_GUARD_IADDR_PHY) {
      bx_phy_address phy;
      bx_bool valid = dbg_xlate_linear2phy(BX_CPU_THIS_PTR guard_found.laddr, &phy);
      if (valid) {
        for (unsigned n=0; n<bx_guard.iaddr.num_physical; n++) {
          if (bx_guard.iaddr.phy[n].enabled && (bx_guard.iaddr.phy[n].addr == phy))
          {
            BX_CPU_THIS_PTR guard_found.guard_found = BX_DBG_GUARD_IADDR_PHY;
            BX_CPU_THIS_PTR guard_found.iaddr_index = n;
            return(1); // on a breakpoint
          }
        }
      }
    }
#endif
  }
#endif

#if BX_GDBSTUB
  if (bx_dbg.gdbstub_enabled) {
    unsigned reason = bx_gdbstub_check(EIP);
    if (reason != GDBSTUB_STOP_NO_REASON) return(1);
  }
#endif

  return(0);
}
#endif // BX_DEBUGGER || BX_GDBSTUB

